package com.mxsoft.concurrent;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author zhangyingxuan
 *
 * 这个程序， 执行， 演示大数组求和的问题
 */
public class ForkJoinDemo {

    public static void main(String[] args) {
        long[] array = new long[4000];
        for (int i = 0; i < array.length; i ++) {
            array[i] = i;
        }

        // 最大并发数4
        ForkJoinPool fjp = new ForkJoinPool(40);

        long start = System.currentTimeMillis();

        SumTask sumTask = new SumTask(array, 0, array.length);
        Long invoke = fjp.invoke(sumTask);

        System.out.println(invoke + " takes times : " + (System.currentTimeMillis() - start) + " mileSeconds");

        start = System.currentTimeMillis();

        invoke = forEvaluate(array);

        System.out.println(invoke + " takes times : " + (System.currentTimeMillis() - start) + " mileSeconds");

        invoke = threadPoolEvaluate(array);

        System.out.println(invoke + " takes times : " + (System.currentTimeMillis() - start) + " mileSeconds");


    }

    static long forEvaluate(long[] array) {
        long sum = 0;
        for (int i = 0; i < array.length ; i ++) {
            sum += array[i];
        }
        return sum;
    }

    static long threadPoolEvaluate(long[] arr) {
        long result = 0;
        ExecutorService executorService = Executors.newFixedThreadPool(4);


        List<Future<Long>> list = new ArrayList<>();
        for (int i = 0; i < arr.length; i+=100) {
            long[] array = new long[100];
            System.arraycopy(arr, i, array, 0, 100);

            MyCallable myCallable = new MyCallable(arr);
            Future<Long> future = executorService.submit(myCallable);
            list.add(future);

        }

        for (Future<Long> future : list) {
            try {
                result += future.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }

        executorService.shutdown();

        return result;


    }

    static class MyCallable implements Callable<Long> {

        long[] arr;
        public MyCallable(long[] arr) {
            this.arr = arr;
        }

        @Override
        public Long call() throws Exception {
            long sum=0;
            for (int i = 0; i < arr.length; i ++) {
                sum+=arr[i];
            }
            return sum;
        }
    }

}

class SumTask extends RecursiveTask<Long> {
    static final int THRESHOLD = 100;

    long[] array;
    int start;
    int end;

    SumTask(long[] array, int start, int end) {
        this.array = array;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        if (end  - start <= THRESHOLD) {
            long sum = 0;
            for (int i = start; i < end; i ++) {
                sum += array[i];
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(String.format("compute %d~%d = %d", start, end, sum));
            return sum;
        }
        int middle = (end + start) / 2;
        System.out.println(String.format("split %d~%d ==> %d~%d, %d~%d", start, end, start, middle, middle, end));
        SumTask sumTask1 = new SumTask(this.array, start, middle);
        SumTask sumTask2 = new SumTask(this.array, middle, end);
        invokeAll(sumTask1, sumTask2);
        Long subResult1 = sumTask1.join();
        Long subResult2 = sumTask2.join();
        return subResult1+subResult2;
    }
}
